/* Last modified Time-stamp: <Tony Xu 2013-11-4 15pm>
 * @(#)simple-shell.c
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_LINE 80 /* The maximum length command */
#ifdef  DEBUG
#define DEBUG_TEST 1
#else
#define DEBUG_TEST 0
#endif

#define debug_print(fmt, ...) \
	do { if (DEBUG_TEST) fprintf(stderr, "%s:%d:%s(): " fmt, __FILE__, \
								 __LINE__, __func__, __VA_ARGS__); } while (0)

/* ----------------------------------------------------------------- */
/* Function Name: parse_cmd_line                                     */
/*     This function parses the command line input, parse each       */
/* command to a string and assign it to a string pointer.            */
/* It takes two parameters, one is a pointer to a string, the other  */
/* one is a array of string pointers **argv, each element in argv    */
/* is pointing to a command. For example, if your input command line */
/* is "ls -a /home/tony", argv[0] = ls, argv[1] = -a and argv[2] =   */
/* /home/tony                                                        */
/* ----------------------------------------------------------------- */
void parse_cmd_line (char *line, char **argv){
	while( *line != '\0' ) {        /* If not the end of the line .......*/

		/* If command line argument start with white space */
		while (*line == ' ' || *line == '\t' || *line == '\n')
			*line++ = '\0';         /* Replace white space with 0 */

		/* Assign the starting position of the command */
        if (*line == 0) {        
            *argv++ = '\0';         /* If null, assign \0 */
        } else {
		    *argv++ = line;         /* Save the argument position */
        }

		/* Looping until hit a while space or new line */
		while (*line != '\0' && *line != ' ' && *line != '\t' && *line != '\n')
			line++;
	}
	*argv = '\0';                    /* Mark the end of argument list */
}

/* ----------------------------------------------------------------- */
/* Function Name: execute                                            */
/*     This function create a child process to execute the input     */
/* command, the input command is passed through an array of pointers */
/* named argv (char). The child process invokes system call execvp() */
/* to execute the command                                            */
/* ----------------------------------------------------------------- */
void execute(char **argv) {
	pid_t pid;
	int status;

	if ((pid = fork()) < 0 ) {
		printf("*** ERROR: forking child process failed\n");
		exit(1);
	}
	else if (pid == 0) {            /* For the child process */
		if (execvp(*argv, argv) < 0) {  
			printf("*** ERROR: exec failed\n");
			exit(1);
		}
	}
	else {
		while (wait(&status) != pid) /* Parent wait for completion */
			;
	}
		 
}

/* ----------------------------------------------------------------- */
/* Function Name: history                                            */
/*    This function print the most recent 10 commands executed in    */
/* this shell                                                        */
void show_history(int h_index, int total_commands, char **history[]) {
	int i, j;
	if ( total_commands <= 10) { /* Print directy if <10 commands */
		for (i = 1; i<=total_commands; i++) {
			printf("%d %s", i, history[i-1]);
		}
	} else {
		/* The coammnds after h_index */
		for (i=0, j=h_index; i<(10-h_index); i++, j++) {
			printf("%d %s", i+1, history[j]);
		}
		/* The commands before h_index */
		for (i=0; i<h_index; i++) {
			printf("%d %s", (10-h_index+i+1), history[i]);
		}
	}
}

/* ----------------------------------------------------------------- */
/*                  The main program starts here                     */
/* ----------------------------------------------------------------- */
int main(void) {
	char line[1024];                /* The input line                         */
	char line2[1024];               /* For history purpose                    */
	char *argv[MAX_LINE/2+1];       /* Command line arguments, maximum 41     */
	char **history[10];             /* History to hold 10 recent commands     */
	int should_run = 1;             /* flag to determine when to exit program */
	int h_index = 0;                /* Index for store the next command       */
	int total_commands = 0;         /* Total number commands inputted         */                   
	
	while (should_run) {            /* Repeat until quit                      */
		h_index = (h_index % 10);
		printf("tosh->");
		fflush(stdout);             /* Flush a given stream                   */
		memset(line, 0, 1024);      
		memset(line2, 0, 1024);
		
		fgets(line, sizeof line, stdin);
		strncpy(line2, line, strlen(line));

		/* check arrary length */
		debug_print("strlen line is %d, line is %s\n", strlen(line), line);
		
		if((strlen(line)>0) && (line[0] != '\n')) { /* Read command line */
			parse_cmd_line(line, argv);             /* parse the line    */
			if (strcmp(argv[0], "quit") == 0 )      /* is it an "quit"?  */
				exit(0);
			if (strcmp(argv[0], "!!") == 0 ) {     /* Run previous command */
				if ( total_commands > 0 ) {
				    printf("Will execute the previous command %s\n", history[h_index-1]);
				    parse_cmd_line(history[h_index-1], argv);
				    execute(argv);
				} else {
					printf("No history commands\n");
				}
			} else if (strcmp(argv[0], "history") == 0) { /* Show all commands */
				show_history(h_index, total_commands, history);
			} else {
				execute(argv);                              /* execute the command */
				/* Allocate space for element */
				history[h_index] = malloc(strlen(line2)*sizeof(char));
				/* Copy the content */
				strncpy(history[h_index], line2, strlen(line2));
				h_index++;
				total_commands++;
			}
		}
 	}
	return 0;
}

